import layout from '../../compute/v1/layout';

import events from '../../compute/v1/events';
import DeskAnimation from '../../compute/v1/deskAnimation';
import { cubicInOut, calc } from '../../../common/animation/index';
import { gradientColor } from '../../../common/color/color';
import { ZRenderComp } from '../../../common/component/zrComp';

import { AbstractContent } from './content';
import { LoadoutContent } from './loadout';
import { NaviContent } from './navi';
import { CodexContent } from './codex';

/**
 * 左下角的那个有特殊效果的小屏幕
 */
class Desktop extends ZRenderComp {
  /** @type {AbstractContent} */
  content = null;
  /** @type {FooterIcon[]} */
  icons = null;
  /** @type {FooterText} */
  compText = null;

  spHeadLine = null;

  constructor(args) {
    super(args.parent);
    this.useParentZRenderInstance(layout.desktop.x, layout.desktop.y);

    /** @type {DeskAnimation} */ this.animation = args.animation;
    this.lineOffset = layout.desktop.footerShapeX;

    this.initBorderLineShape();
    this.initContentShape();

    this.on(events.FOOTER_SWITCH, this.onFooterSwitch.bind(this));
  }

  init() {
    if (this.content == null) {
      this.createContent();
    }

    const {zr, zrender, animation} = this;
    const [x, y] = this.relativePos();

    // 头: 线条
    const dw = layout.desktop.width;
    zr.add(this.spHeadLine = zrender.path.createFromString(
      `M0,16v-4h4v-4h4v-4h4v-4h${dw-24}v4h4v4h4v4h4v4h-4v-4h-4v-4h-4v-4h-${dw-24}v4h-4v4h-4v4h-4z`,
      {
        style: {
          fill: layout.colors.strokeNormal,
          shadowBlur: 5,
          shadowColor: layout.colors.strokeBgL1,
        },
        position: [x, y], zlevel: 50
      }
    ));

    // 透明背景
    this.lastFooterOffset = animation.footerShapeOffset;
    zr.add(this.spBg = new this.contentShape({
      shape : { footOffset : animation.footerShapeOffset },
      style : {
        fill: new zrender.LinearGradient(
          0, 0, 0, 1,
          [
            {offset:0.04, color: layout.colors.strokeBgD2},
            {offset:0.12, color: layout.colors.strokeBg},
            {offset:0.65, color: layout.colors.strokeBgL1},
            {offset:0.86, color: layout.colors.strokeBg},
            {offset:0.94, color: layout.colors.strokeBgD1},
          ]
        ),
        // stroke: ''
        opacity: 0.7,
        blend: 'overlay',
      },
      position: [x, y], zlevel: 20, z: 0
    }));

    // 尾巴: 图标
    this.icons = animation.icons.map((iconName, index) => new FooterIcon({
      parent : this, iconName, index, animation
    }));

    // 尾巴: 文字
    this.compText = new FooterText({
      animation, parent : this, title : layout.iconPath.loadout.title
    });

    // 尾巴: 线条
    this.spBorderLine = new this.borderLineShape({
      shape: {
        width: layout.desktop.footerWidth
      },
      style: {
        fill: layout.colors.strokeNormal,
        shadowBlur: 5,
        shadowColor: layout.colors.strokeBgL1,
      },
      position: [x + layout.desktop.footerX, y + layout.desktop.footerY], zlevel: 50
    });
    zr.add(this.spBorderLine);
    animation.footerShapeOffset = layout.desktop.footerShapeX;
    animation.onFooterSwitch = () => {
      this.emit(events.FOOTER_SWITCH, animation);
      this.emit(events.STATUS_REPORT_TO_SERVER);
    }
  }

  /**
   * @param {number} x
   * @param {number} y
   * @param {number} w width
   * @param {number} lw lineWidth
   * @param {number} mw mouthWidth
   * @param {number} mh mouthHeight
   * @param {number} o offset
   */
  borderLinePath(x, y, w, lw, mw, mh, o, path) {
    function rect(path, xx, yy, ww, hh) {
      path.moveTo(xx, yy);
      path.lineTo(xx + ww, yy);
      path.lineTo(xx + ww, yy + hh);
      path.lineTo(xx, yy + hh);
      path.lineTo(xx, yy);
      path.closePath();
    }
    rect(path, x, y, o, lw); // 左长线
    rect(path, x+o+mw, y, w-o-mw, lw); // 右长线
    for (let i = 0; i < mh; i += lw) {
      rect(path, x-i, y-i, -lw, -lw); // 左上
      rect(path, x+o+i, y+lw+i, lw, lw); // mouth 左
      rect(path, x+o+mw-i, y+lw+i, -lw, lw); // mouth 右
      rect(path, x+w+i, y-i, lw, -lw); // 右上
    }
  }

  initBorderLineShape() {
    const pathBuilder = this.borderLinePath.bind(this);

    this.borderLineShape = this.zrender.Path.extend({
      type: 'borderLine',
      shape: {
        // 顶部的中心位置
        x: 0,
        y: 0,
        width: 500,
        lineWidth: 4,
        mouthWidth: 50,
        mouthHeight: 12,
        offset: 100,
      },
      buildPath: function (path, shape) {
        const {x, y, width : w, lineWidth : lw, mouthWidth : mw, mouthHeight : mh, offset : o} = shape;
        pathBuilder(x, y, w, lw, mw, mh, o, path);
      },
      style: {
        stroke: 'transparent',
        fill: '#FFF'
      }
    });
  }

  initContentShape() {
    this.contentShape = this.zrender.Path.extend({
      type: 'content',
      shape: {
        width: layout.desktop.width,
        height: layout.desktop.height,
        footOffset: 100,
      },
      buildPath: function (path, shape) {
        layout.contentShape.build(path, shape.footOffset, shape.width, shape.height);
      },
      style: {}
    });
  }

  onFooterSwitch() {
    const {animation} = this;
    const layIconPath = layout.iconPath;

    this.createContent();

    this.compText = new FooterText({
      animation, parent : this,
      title : [layIconPath.loadout, layIconPath.codex, layIconPath.navi][animation.index].title
    });
  }

  createContent() {
    switch (this.animation.index) {
    case 0:
      this.content = new LoadoutContent(this, this.zrender, this.animation);
    break;
    case 1:
      this.content = new CodexContent(this, this.zrender, this.animation);
    break;
    case 2:
      this.content = new NaviContent(this, this.zrender, this.animation);
    break;
    }
  }

  tick(now) {
    const { animation } = this;
    const lay = layout.desktop;

    // animation
    animation.time = now - animation.baseTime;
    if (animation.footerShapeOffset != this.lastFooterOffset) {
      this.lastFooterOffset = animation.footerShapeOffset;
      this.makeDirty(false);
    }

    // 500ms, offset 从 lastIndex 指向的位置变道 index 指向的位置
    const toX = lay.footerShapeX + animation.index * lay.iconOffsetX;
    if (toX != this.lineOffset) {
      const fromX = lay.footerShapeX + animation.lastIndex * lay.iconOffsetX;
      animation.footerShapeOffset = this.lineOffset = calc(fromX, toX, animation.time / 500, cubicInOut);
      this.makeDirty(false);
    }

    super.tick(now);
  }

  paint() {
    this.spBorderLine.attr({
      shape : {
        offset : this.lineOffset
      }
    });

    this.spBg.attr({
      shape : {
        footOffset : this.animation.footerShapeOffset
      }
    });
  }
}

class FooterIcon extends ZRenderComp {
	constructor(args) {
		super(args.parent);
		/** @type {string} */ this.iconName = args.iconName;
		/** @type {number} */ this.index = args.index;
		/** @type {DeskAnimation} */ this.animation = args.animation;

		const lay = layout.desktop;
		this.leftX = lay.iconLeftX + this.index * lay.iconOffsetX;
		this.rightX = lay.iconRightX - (this.animation.icons.length - this.index - 1) * lay.iconOffsetX;
		this.useParentZRenderInstance(this.index <= this.animation.index ? this.leftX : this.rightX, lay.iconY);

		const pathParam = layout.iconPath[this.iconName];
		/** @type {string} */ this.path = pathParam.path;
		/** @type {number} */ this.pathWidth = pathParam.width;
		/** @type {number} */ this.pathHeight = pathParam.height;

		this.lightColor = layout.colors.yellowNormal;
		this.darkColor = layout.colors.strokeBgL1;
		this.iconColor = this.animation.index == this.index ? this.lightColor : this.darkColor;

		this.on(events.FOOTER_SWITCH, this.onFooterSwitch.bind(this));
	}

	onFooterSwitch() {
		const fromLeft = this.index <= this.animation.lastIndex;
		const toLeft = this.index <= this.animation.index;
		this.clearAnimations();
		if (fromLeft && !toLeft) {
			this.getAnimX().update()
				.when(0, this.leftX)
				.when(500, this.rightX).easing(cubicInOut)
				.commit();
		} else if (!fromLeft && toLeft) {
			this.getAnimX().update()
				.when(0, this.rightX)
				.when(500, this.leftX).easing(cubicInOut)
				.commit();
		}

		if (this.index == this.animation.index) {
			this.createAnimation('color')
				.whenUpdated(p => this.updateColor(p.value))
				.update()
				.when(0, 0)
				.when(500, 1).easing(cubicInOut)
				.commit();
		} else if (this.index == this.animation.lastIndex) {
			this.createAnimation('color')
				.whenUpdated(p => this.updateColor(p.value))
				.update()
				.when(0, 1)
				.when(500, 0).easing(cubicInOut)
				.commit();
		}
	}

	/**
	 * @param {number} delta 0 表示暗, 1 表示亮
	 */
	updateColor(delta) {
		this.iconColor = gradientColor(this.darkColor, this.lightColor, delta).toHexRGBA();
		this.makeDirty(false);
	}

	init() {
		const {path, pathWidth, pathHeight, iconColor} = this;
		this.spIcon = this.createPathFromString(path)
			.fill(iconColor)
			.shadow(iconColor, 8)
			.position(- pathWidth / 2, - pathHeight / 2)
			.z(20, 0)
			.build();
	}

	paint() {
		const {pathWidth, pathHeight, iconColor} = this;
		const [x, y] = this.relativePos();
		this.spIcon.sp.attr({
			position : [x - pathWidth / 2, y - pathHeight / 2],
			style : {
				fill : iconColor,
				shadowColor: iconColor,
			}
		});
	}
}

class FooterText extends ZRenderComp {
	sps = [];

	constructor(args) {
		super(args.parent);
		/** @type {string} */ this.title = args.title;
		/** @type {DeskAnimation} */ this.animation = args.animation;

		const lay = layout.desktop;
		this.useParentZRenderInstance(lay.textLeftX + this.animation.index * lay.iconOffsetX, lay.iconY - 6);

		this.on(events.FOOTER_SWITCH, this.onFooterSwitch.bind(this));
	}

	init() {
		const {title} = this;

		let xx = 0;
		for (let i = 0; i < title.length; i++) {
			let ch = title[i];
			const {path, width} = layout.charPath[ch];
			this.sps.push(this.initText(i, xx, path));

			xx += (width * 1.5 + 4);
		}
	}

	initText(index, x, path) {
		let s = this.createPathFromString(path)
			.fill(layout.colors.yellowNormal)
			.shadow(layout.colors.yellowNormal, 3)
			.opacity(0)
			.position(x, 0)
			.scale(1.5, 1.5)
			.z(20, 202)
			.build();
		this.createAnimation('opacity-' + index)
			.whenUpdated(p => s.opacity(p.value))
			.update()
			.when(index * 50, 0)
			.when(300 + index * 50, 1)
			.commit();
		return s;
	}

	onFooterSwitch() {
		this.clearAnimations();
		this.sps.forEach((sp, index) => this.wipeText(sp, index));
	}

	wipeText(s, index) {
		this.createAnimation('wipe-' + index).whenUpdated(p => s.opacity(p.value))
			.update()
				.when(index * 50, 1)
				.when(300 + index * 50, 0).callback(() => (index == this.sps.length - 1) && this.destroy())
			.commit();
	}
}

export { Desktop };
